gtkwindow: Use window-manager-side window menus
authorJasper St. Pierre <jstpierre@mecheye.net>
Thu, 13 Mar 2014 21:28:01 +0000 (17:28 -0400)
committerJasper St. Pierre <jstpierre@mecheye.net>
Wed, 21 May 2014 22:41:07 +0000 (18:41 -0400)
This avoids a bunch of policy problems with deciding how to lay
out the window menu under different WMs.

For now, we use the special event _GTK_SHOW_WINDOW_MENU, but we
hope to have this standardized in wm-spec quite soon, as KDE wants
it as well.

gdk/gdkwindow.c
gdk/gdkwindow.h
gdk/gdkwindowimpl.h
gdk/x11/gdkwindow-x11.c
gtk/gtkwindow.c

index e50601ab3254d4cfa70db30b1bba86db202dffb4..7f72dca18055772b04f567284ce1fc2520a0891a 100644 (file)
@@ -10921,3 +10921,36 @@ gdk_window_set_shadow_width (GdkWindow *window,
   if (impl_class->set_shadow_width)
     impl_class->set_shadow_width (window, left, right, top, bottom);
 }
+
+/**
+ * gdk_window_show_window_menu:
+ * @window: a #GdkWindow
+ * @event: a #GdkEvent to show the menu for
+ *
+ * Asks the window menu to show the window menu. The window menu is
+ * the menu shown when right-clicking the titlebar on traditional
+ * windows managed by the window manager. This is useful for windows
+ * using client-side decorations, activating it with a right-click
+ * on the window decorations.
+ *
+ * Returns: %TRUE if the window menu was shown by the window
+ * manager and %FALSE otherwise.
+ *
+ * Since: 3.14
+ */
+gboolean
+gdk_window_show_window_menu (GdkWindow *window,
+                             GdkEvent  *event)
+{
+  GdkWindowImplClass *impl_class;
+
+  g_return_val_if_fail (GDK_IS_WINDOW (window), FALSE);
+  g_return_val_if_fail (!GDK_WINDOW_DESTROYED (window), FALSE);
+
+  impl_class = GDK_WINDOW_IMPL_GET_CLASS (window->impl);
+
+  if (impl_class->show_window_menu)
+    return impl_class->show_window_menu (window, event);
+  else
+    return FALSE;
+}
index d6c72052a37f6f7d44b6ef7af28500521cbcea4e..09a4af3d2b4589506fea821bee5e0b7055ccc95b 100644 (file)
@@ -1100,6 +1100,9 @@ void       gdk_window_set_shadow_width         (GdkWindow      *window,
                                                 gint            right,
                                                 gint            top,
                                                 gint            bottom);
+GDK_AVAILABLE_IN_3_14
+gboolean  gdk_window_show_window_menu          (GdkWindow      *window,
+                                                GdkEvent       *event);
 
 G_END_DECLS
 
index bd1714d7f710145598a9b0c02ac74574409cacf9..b1e9290bc15d005b1021354b57c170aeeb6b0de4 100644 (file)
@@ -301,6 +301,8 @@ struct _GdkWindowImplClass
                                            gint            right,
                                            gint            top,
                                            gint            bottom);
+  gboolean     (* show_window_menu)       (GdkWindow      *window,
+                                           GdkEvent       *event);
 };
 
 /* Interface Functions */
index e5e0b232523168dddd80e78c18ce4fb2f17d4600..d3120a65ddedb6ab90e072137fc3d56daa907430 100644 (file)
@@ -5702,6 +5702,47 @@ gdk_x11_window_set_opaque_region (GdkWindow      *window,
     g_free (data);
 }
 
+static gboolean
+gdk_x11_window_show_window_menu (GdkWindow *window,
+                                 GdkEvent  *event)
+{
+  GdkDisplay *display = GDK_WINDOW_DISPLAY (window);
+  GdkDevice *device;
+  int device_id;
+  XClientMessageEvent xclient = { 0 };
+
+  switch (event->type)
+    {
+    case GDK_BUTTON_PRESS:
+    case GDK_BUTTON_RELEASE:
+      break;
+    default:
+      return FALSE;
+    }
+
+  if (!gdk_x11_screen_supports_net_wm_hint (GDK_WINDOW_SCREEN (window),
+                                            gdk_atom_intern_static_string ("_GTK_SHOW_WINDOW_MENU")))
+    return FALSE;
+
+  device = gdk_event_get_device (event);
+
+  g_object_get (G_OBJECT (device),
+                "device-id", &device_id,
+                NULL);
+
+  xclient.type = ClientMessage;
+  xclient.window = GDK_WINDOW_XID (window);
+  xclient.message_type = gdk_x11_get_xatom_by_name_for_display (display, "_GTK_SHOW_WINDOW_MENU");
+  xclient.data.l[0] = device_id;
+  xclient.format = 32;
+
+  XSendEvent (GDK_DISPLAY_XDISPLAY (display), GDK_WINDOW_XROOTWIN (window), False,
+              SubstructureRedirectMask | SubstructureNotifyMask,
+              (XEvent *)&xclient);
+
+  return TRUE;
+}
+
 static void
 gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
 {
@@ -5791,4 +5832,5 @@ gdk_window_impl_x11_class_init (GdkWindowImplX11Class *klass)
   impl_class->get_scale_factor = gdk_x11_window_get_scale_factor;
   impl_class->set_opaque_region = gdk_x11_window_set_opaque_region;
   impl_class->set_shadow_width = gdk_x11_window_set_shadow_width;
+  impl_class->show_window_menu = gdk_x11_window_show_window_menu;
 }
index 20f7251e29c53b19ccc87f304cd6eeb0758c2703..537cdf1fe6495c25a49cf4e2798be203299719c4 100644 (file)
@@ -8632,48 +8632,6 @@ popup_position_func (GtkMenu   *menu,
 {
 }
 
-static void
-ontop_window_clicked (GtkMenuItem *menuitem,
-                      gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-
-  gtk_window_set_keep_above (window, !window->priv->above_initially);
-}
-
-#ifdef GDK_WINDOWING_X11
-static void
-stick_window_clicked (GtkMenuItem *menuitem,
-                      gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-
-  gtk_window_stick (window);
-}
-
-static void
-unstick_window_clicked (GtkMenuItem *menuitem,
-                        gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-
-  gtk_window_unstick (window);
-}
-
-static void
-workspace_change_clicked (GtkMenuItem *menuitem,
-                          gpointer     user_data)
-{
-  GtkWindow *window = (GtkWindow *)user_data;
-  GdkWindow *gdk_window;
-  guint32 desktop;
-
-  gdk_window = gtk_widget_get_window (GTK_WIDGET (window));
-  desktop = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (menuitem), "workspace"));
-  gdk_x11_window_move_to_desktop (gdk_window, desktop);
-}
-#endif
-
 static void
 close_window_clicked (GtkMenuItem *menuitem,
                       gpointer     user_data)
@@ -8685,30 +8643,8 @@ close_window_clicked (GtkMenuItem *menuitem,
 }
 
 static void
-move_window_clicked (GtkMenuItem *menuitem,
-                     gpointer     user_data)
-{
-  GtkWidget *widget = (GtkWidget *)user_data;
-
-  gdk_window_begin_move_drag (gtk_widget_get_window (widget),
-                              0, 0, 0,
-                              gtk_get_current_event_time ());
-}
-
-static void
-resize_window_clicked (GtkMenuItem *menuitem,
-                       gpointer     user_data)
-{
-  GtkWidget *widget = (GtkWidget *)user_data;
-
-  gdk_window_begin_resize_drag (gtk_widget_get_window (widget),
-                                0, 0, 0, 0,
-                                gtk_get_current_event_time ());
-}
-
-static void
-gtk_window_do_popup (GtkWindow      *window,
-                     GdkEventButton *event)
+gtk_window_do_popup_fallback (GtkWindow      *window,
+                              GdkEventButton *event)
 {
   GtkWindowPrivate *priv = window->priv;
   GtkWidget *menuitem;
@@ -8724,129 +8660,6 @@ gtk_window_do_popup (GtkWindow      *window,
                              GTK_WIDGET (window),
                              popup_menu_detach);
 
-  menuitem = gtk_menu_item_new_with_label (_("Minimize"));
-  gtk_widget_show (menuitem);
-  if (priv->gdk_type_hint != GDK_WINDOW_TYPE_HINT_NORMAL)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  g_signal_connect_swapped (G_OBJECT (menuitem), "activate",
-                            G_CALLBACK (gtk_window_iconify), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_menu_item_new_with_label (priv->maximized ? _("Unmaximize") : _("Maximize"));
-  gtk_widget_show (menuitem);
-  if (!priv->resizable ||
-      priv->gdk_type_hint != GDK_WINDOW_TYPE_HINT_NORMAL)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  g_signal_connect_swapped (G_OBJECT (menuitem), "activate",
-                            G_CALLBACK (_gtk_window_toggle_maximized), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_menu_item_new_with_label (_("Move"));
-  gtk_widget_show (menuitem);
-  g_signal_connect (G_OBJECT (menuitem), "activate",
-                    G_CALLBACK (move_window_clicked), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_menu_item_new_with_label (_("Resize"));
-  gtk_widget_show (menuitem);
-  if (!priv->resizable || priv->maximized)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  g_signal_connect (G_OBJECT (menuitem), "activate",
-                    G_CALLBACK (resize_window_clicked), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_separator_menu_item_new ();
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-  menuitem = gtk_check_menu_item_new_with_label (_("Always on Top"));
-  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), priv->above_initially);
-  if (priv->maximized)
-    gtk_widget_set_sensitive (menuitem, FALSE);
-  gtk_widget_show (menuitem);
-  g_signal_connect (G_OBJECT (menuitem), "activate",
-                    G_CALLBACK (ontop_window_clicked), window);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-#ifdef GDK_WINDOWING_X11
-  if (GDK_IS_X11_DISPLAY (gtk_widget_get_display (GTK_WIDGET (window))))
-    {
-      menuitem = gtk_check_menu_item_new_with_label (_("Always on Visible Workspace"));
-      gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menuitem), TRUE);
-      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), priv->stick_initially);
-      gtk_widget_show (menuitem);
-      g_signal_connect (G_OBJECT (menuitem), "activate",
-                        G_CALLBACK (stick_window_clicked), window);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-      menuitem = gtk_check_menu_item_new_with_label (_("Only on This Workspace"));
-      gtk_check_menu_item_set_draw_as_radio (GTK_CHECK_MENU_ITEM (menuitem), TRUE);
-      gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (menuitem), !priv->stick_initially);
-      gtk_widget_show (menuitem);
-      g_signal_connect (G_OBJECT (menuitem), "activate",
-                        G_CALLBACK (unstick_window_clicked), window);
-      gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
-      if (!priv->stick_initially)
-        {
-          guint32 n_desktops, desktop;
-
-          n_desktops = gdk_x11_screen_get_number_of_desktops (gtk_widget_get_screen (GTK_WIDGET (window)));
-          desktop = gdk_x11_window_get_desktop (gtk_widget_get_window (GTK_WIDGET (window)));
-
-          if (desktop > 0)
-            {
-              menuitem = gtk_menu_item_new_with_label (_("Move to Workspace Up"));
-              g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (desktop - 1));
-              gtk_widget_show (menuitem);
-              g_signal_connect (G_OBJECT (menuitem), "activate",
-                                G_CALLBACK (workspace_change_clicked), window);
-              gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-            }
-          if (desktop + 1 < n_desktops)
-            {
-              menuitem = gtk_menu_item_new_with_label (_("Move to Workspace Down"));
-              g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (desktop + 1));
-              gtk_widget_show (menuitem);
-              g_signal_connect (G_OBJECT (menuitem), "activate",
-                                G_CALLBACK (workspace_change_clicked), window);
-              gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-            }
-          if (n_desktops > 2)
-            {
-              GtkWidget *submenu;
-              gint d;
-              guint32 current;
-
-              current = gdk_x11_screen_get_current_desktop (gtk_widget_get_screen (GTK_WIDGET (window)));
-              menuitem = gtk_menu_item_new_with_label (_("Move to Another Workspace"));
-              gtk_widget_show (menuitem);
-              gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-              submenu = gtk_menu_new ();
-              gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuitem), submenu);
-              for (d = 0; d < n_desktops; d++)
-                {
-                  gchar *label;
-                  label = g_strdup_printf (_("Workspace %d"), d + 1);
-                  menuitem = gtk_menu_item_new_with_label (label);
-                  g_free (label);
-                  g_object_set_data (G_OBJECT (menuitem), "workspace", GUINT_TO_POINTER (d));
-                  if (d == current)
-                    gtk_widget_set_sensitive (menuitem, FALSE);
-                  gtk_widget_show (menuitem);
-                  g_signal_connect (G_OBJECT (menuitem), "activate",
-                                    G_CALLBACK (workspace_change_clicked), window);
-                  gtk_menu_shell_append (GTK_MENU_SHELL (submenu), menuitem);
-                }
-            }
-        }
-    }
-#endif
-
-  menuitem = gtk_separator_menu_item_new ();
-  gtk_widget_show (menuitem);
-  gtk_menu_shell_append (GTK_MENU_SHELL (priv->popup_menu), menuitem);
-
   menuitem = gtk_menu_item_new_with_label (_("Close"));
   gtk_widget_show (menuitem);
   if (!priv->deletable)
@@ -8867,6 +8680,15 @@ gtk_window_do_popup (GtkWindow      *window,
                     0, gtk_get_current_event_time ());
 }
 
+static void
+gtk_window_do_popup (GtkWindow      *window,
+                     GdkEventButton *event)
+{
+  if (!gdk_window_show_window_menu (gtk_widget_get_window (GTK_WIDGET (window)),
+                                    (GdkEvent *) event))
+    gtk_window_do_popup_fallback (window, event);
+}
+
 /*********************************
  * Functions related to resizing *
  *********************************/